﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Djs.Neuronic.Core;
using Djs.Neuronic.Medium;

namespace Djs.Neuronic.Brain
{
    /// <summary>
    /// Obecné centrum řeči, obsahuje různé jazyky.
    /// </summary>
    public partial class SpeechCls : CommonCls
    {
        #region Konstrukce a SelfTest
        protected override void Init()
        {
            this.Words = new WordDataTable(this.Creator);
            this.Languages = new Dictionary<int, LanguageCls>();
        }
        internal override void Prepare()
        {
            this.MainLanguage = new LanguageCls(this, "cz");
        }
        public WordDataTable Words { get; private set; }
        public LanguageCls MainLanguage { get; private set; }
        private Dictionary<int, LanguageCls> Languages;
        #region SelfTest
        internal override void SelfTest()
        {
            #region sample text
            string sample = @"Přednáška 2: Morfologie neuronu (dendrity a axon, synapse), synaptický přenos 
(skladování, výlev a zpětné vychytávání neuropřenašečů), neuropřenašečové systémy v 
mozku
 Normální funkci mozkové tkáně zajišťuje několik buněčných typů. Dnes se zastavíme 
u buněk, jejichž funkce je podporována neurogliemi, a to ve smyslu myelinizace, sekrece 
trofických faktorů, udržování extracelulárního milieu a „odklízení“ buněčných a 
metabolických zbytků z něj. Takto opečovávaným typem buněk jsou neurony :) 
Zopakujme si z minulé přednášky: 
Neuron je vysoce polarizovaná komunikační buňka vyskytující se v rámci nervového 
systému ve velkém množství subtypů, propojených vzájemně do okruhů. Termínem „vysoce 
polarizovaná buňka“ míním to, že v rámci dozrávání se na neuronu profilují subcelulární 
domény (oblasti) s různou specializovanou funkcí. Morfologicky pak lze v typickém neuronu 
pozorovat tři hlavní oblasti: 
• tělo neuronu (perikaryon), obsahující jádro a hlavní cytoplasmatické organely; 
• různý počet dendritů, které odstupují z perikaryonu, liší se velikostí, tvarem, 
přítomností dendritických trnů a větví se různě daleko a hustě do šedé hmoty; a 
• jediný axon, který u většiny axonů odstupuje z perikaryonu do daleko větší 
vzdálenosti než kterákoliv z větví dendritické stromu. 
Počet neuronů se mezidruhově velmi dramaticky liší. Zatímco hlístice Caenorhabditis 
elegans má jen 302 neuronů, Drosophila melanogaster jich vlastní cca 100 000. Obvyklé 
číslo spojované 
s dospělým lidským 
mozkem je 1011 neuronů
(sto miliard), přičemž 
každý jeden neuron je 
vybaven v průměru 7 
000 synaptickými 
spojeními s dalšími 
neurony. Tříleté dítě má 
asi 1015 synapsí. Tento 
počet klesá s věkem a u 
dospělého člověka je 
odhadován na 1014 až 5 
×1014 synapsí. Jsou 
autoři, kteří pro dospělý 
lidský mozek udávají počet 
cca 86 miliard neuronů, 
z nich 16.3 miliardy je 
v kůře mozkové a 69 miliard 
v mozečku. Z této práce 
(Azevedo et al. 2009) je i 
následující obrázek a graf. 
Na obrázku vidíte -možná 
překvapivou- předpokládanou distribuci neuronů a non-neuronálních buněk v lidském mozku. Všimněte si, 
že majorita neuronů se nachází v mozečku, kde je jich cca 4 × více než non-neuronálních 
buněk. V kortexu je podle této práce poměr neuronů a non-neuronálních buněk opačný. 
Ale zpět k jedinému neuronu. 
Velikost perikaryonu se může mezi jednotlivými neuronálními subtypy značně lišit. 
Nejmenší perikaryon je popsán u granulárních 
buněk mozečku (průměr asi 5 µm), zatímco 
největší těla neuronů patří Betzovým buňkám 
mozku (průměr asi 100 µm). Z pohledu neuronu 
je perikaryon jeho nejmenší částí – většina 
cytoplasmatického obsahu je distribuována do 
axonu a dendritického stromu. Perikaryon je 
bohatý na cisterny endoplasmatického retikula, 
proloženými řetězci volných polyribosomů
označovaných jako Nisslova substance. 
Vzhledem k tomu, že musí vyprodukovat 
veškeré látky potřebné pro fungování a přežití 
neuronu, obsahuje obrovské množství mitochondrií, zdrojů ATP. Dalším typickým znakem 
těla neuronu je přítomnost bohatého cytoskeletu (neurfilament a mikrotubulů), který ve 
vřetenech vybíhá do dendritů a axonů. Podle počtu z těla neuronu odstupujících výběžků lze 
morfologicky dělit neurony na unipolární, bipolární či multipolární: 
• dendrit a axon unipolárních (pseudounipolárních) neuronů odstupuje ze stejného 
výběžku; 
• u bipolárních neuronů odstupují axon a jeden dendrit na opačných koncích 
perikaryonu; 
• multipolární neurony mají více než dva dendrity a morfologicky se dělí na 
- multipolární neurony Golgiho typu I s dlouhým axonem (pyramidové buňky, 
Purkyněho buňky, buňky předních rohů míšních) 
- multipolární neurony Golgiho typu II, jejichž axony projikují lokálně
(granulární buňky) 
Morfologie axonu a jeho cesty nervovou tkání závisí na typu informace, kterou přenáší, a na 
propojení neuronu s dalšími neurony. Axon bývá v případě dlouhých projekcí myelinizovaný; 
v případě, kdy 
zasahuje jen do 
blízkého okolí 
neuronu, však 
myelinovou 
pochvu mít 
nemusí. Někdy 
může být těžké 
morfologicky 
odlišit axon od 
hlavních větví 
dendritického 
stromu, nicméně
axony vykazují
několik typických znaků. (i) Průměr axonu je po celé délce víceméně konstantní, oproti tomu 
dendritů klesá směrem od hlavní větve k větvím vyššího řádu. (ii) Dendrity obsahují organely, 
které axon nemá. (iii) Dendrity a axon lze odlišit díky specifickým axonálním a dendritickým 
proteinům. 
Axon z těla neuronu odstupuje v místě nazývaném axonální hrbolek (axon hillock); 
tato struktura je zvláště dobře patrná u velkých pyramidových buněk. U ostatních 
neuronálních typů axon často vybíhá z jednoho z hlavních dendritů. Axony obvykle obsahují 
více neurofilament než dendrity a bývají často značně rozvětvené – např. neurony propojující 
různé kortikální oblasti mají velký počet kolaterál. Naopak, třeba kortikální motorické 
neurony mají axon v podstatě přímě spojující subkortikální centra s předními rohy míšními. 
 Axony jsou zodpovědné za přenos informace zpracované neurony. Rozhraní mezi 
axonálním zakončením (terminálou, terminální arborizací) a cílovou buňkou tvoří synapse. 
Synapse je specializovaný útvar obecně složený s presynaptického (axonálního) elementu, 
úzké synaptické štěrbiny a elementu postsynaptického. Podle polohy elementu pre- a 
postsynaptického se obvykle rozlišují 
• axodendritické či axosomatické synapse, jejichž presynaptický element inervuje 
dendrity nebo tělo jiného neuronu; 
• axoaxonální synapse, na kterých se neuropřenašeč vylévá poblíž jiné nervové 
terminály; 
• autoreceptorové „synapse“ ležící na tom samém nervovém zakončení, z něhož se 
neuropřenašeč vylévá; 
• dendroaxonální synapse, na nichž neuropřenašeč uvolněný z dendritů či těla neuronu 
ovlivňuje zakončení axonu neuronu jiného (vzácné); 
• dendrodenritické synapse vyskytující se např. v čichovém laloku či sítnici 
• někdy mohou vzniknout synapse i mezi dvěma perikaryony; a dále známe 
• zakončení en passant, označované i jako synapse en passant, což jsou spoje 
„mimochodem“ se utvářející při průchodu axonu nad dendritickým stromem jiného 
postsynaptického neuronu. 
Jak už jsme se zmínili,někdy není snadné odlišit axon od jiné výběžkovité struktury – 
dendritu (δένδρον [déndron] = strom). Dendrity jsou mnohočetné jemné výběžky, které 
odstupují z těla neuronu a spolu s perikaryonem představují primární recepční strukturu, 
dostávající informace ze synaptických vstupů jiných neuronů. Jejich funkce kromě recepce 
zahrnují též procesování a integraci této vstupní informace. Dendrity se větví ve výběžky 
vyšších řádů, přičemž vytvářejí tzv. dendritický strom. Geometrie dendritického stromu může 
být velice komplexní a často velmi půvabná :) Poloha určitého dendritu v rámci dendritického 
stromu determinuje do značné míry i jeho příspěvek k celkovému informačnímu vstupu, 
pokud je „vybaven“ přibližně stejným množstvím stejných synapsí, neboť šíření vzruchu po 
dendritech a těle neuronu je převážně elektrotonické (dendrity ale mají i napěťově ovládané 
iontové kanály a jsou schopny generovat akční potenciál, jsou tedy schopny i aktivního šíření 
vzruchu). Čím dále pak takový dendrit od integrujícího místa leží, tím je jeho příspěvek nižší 
než příspěvek stejně „vybaveného“ dendritu, odstupujícího blíže u těla neuronu. 
Z dendritů mnoha buněk odstupují malé membránové protruze – dendritické trny. Tyto 
trny dostávají zejména excitační synaptické vstupy určené jejich neuronu, nicméně mohou na 
hlavičce obsahovat i aditivní synaptické vstupy. Mohou strukturálně obklopovat a 
biochemicky izolovat danou synapsi, takže mohou fungovat jako velmi samostatné 
informační jednotky. Pro neuron je velmi výhodné, když takto kompartmentalizuje 
biochemickou signalizaci: odděluje vstupy pro jednu synapsi s minimální interferencí
s informací pro synapsi jinou. Takto „informačně nejoddělenější“ jsou dlouhé, tenké 
dendritické trny. 
Dendritické trny pozoroval už Cajal (!). Při impregnaci řezů
stříbrem (Golgiho metoda) je dokázal na řezech detekovat, ačkoliv 
jiní badatelé měli za to, že jde jen o metodický artefakt a do svých 
nákresů je nezahrnovali (včetně samotného Golgiho). Cajal zavedl 
metodiku barvení methylénovou modří a dendritické trny pozoroval 
stále, čímž prokázal, že nejde o superficiální precipitáty na sklíčku, 
ale ačkoliv měl naprosto stejné vybavení jako jeho kolegové a 
konkurenti, nikdo z nich nedokázal jeho pozorování verifikovat. 
Dendritické trny se nacházejí na všech hlavních neuronálních 
typech v CNS (kortikální neurony, Purkyněho buňky atd.). Dendrity 
jednoho neuronu mohou souhrnně obsahovat stovky až tisíce trnů. 
Typické hustota trnů může dosahovat až 20 trnů na10 µm délky 
dendritu. Dendritické trny kortikálních neuronů mohou obsahovat až 
desítky tisíc synapsí, Purkyněho buňky i o řád více. (Průměrný počet dendritických trnů na 
jedné dendritické větvi byl stanoven na 61 000.) 
Trny jsou útvary složené z „hlavičky“ a nožky, která je spojuje s dendritem. Objem 
hlavičky je obvykle 0,01-0,8 µm3
 . Podle tvaru hlavičky se trny obvykle dělí na ""tenké - 
thin"", ""pahýlovité (useknuté) - stubby"", ""hřibovité - mushroom"", a ""větvené - branched"". 
jejich membrána bývá vybavena AMPA či NMDA receptory (excitační transmise - 
glutamátové receptory). Obsahují i TrkB receptor pro BDNF (udržení trnu na dendritu). 
Dendritické trny jsou velmi plastické – vejmi výrazně mění svůj tvar, objem a počet 
v relativně krátkém časovém úseku. Jejich dynamičnost je podložena aktinovým 
cytoskeletem, takže jsou schopny remodelace v řádu vteřin až minut. Počet dendritických trnů
se také výrazně v čase mění – 10-20% trnů v pyramidových buňkách kortexu může vznikat a 
zanikat v rádu hodin, „Hřibovité“ trny jsou nejstabilnější. Plasticity dendritických trnů je 
využíváno např. při tvorbě paměťových stop; např. v případě dlouhodobé paměti byl 
pozorován růst nových dendritických trnů či prodlužování stávajících, aby byla posílena 
schopnost presynaptického neuronu aktivovat ten postsynaptický a aby došlo k posílení 
synaptického kontaktu. 
Počet a tvar dendritických trnů kolísá i v závislosti 
na různých patofyziologických stavech. V případě
syndromu fragilního chromosomu X je jedním z projevů
mentální retardace, pravděpodobně podložená chyběním 
proteinu FMRP (fragile X mental retardation protein). 
Díky absenci tohoto proteinu vzniká neúměrně mnoho 
dendritických trnů. FMRP normálně funguje jako 
„brzda“ vzniku nových synapsí a vyvažuje „posilující“ 
efekt metabotropních glutamátových receptorů
(mGluR5), které vznik synapsí na trnech potencují. Na 
obrázku vidíte dendrit s trny zdravé myši (vlevo), myši 
s nedostatkem FMRP (uprostřed) a myší rovněž knockoutované pro FMRP, které byl ovšem uměle redukován 
i počet mGluR5, takže nedošlo tak dramatickému nárostu počtu dendritických trnů jako při 
prostém knock-outu pro FMRP. ";
            string text = @"Velikost perikaryonu. se může mezi jednotlivými neuronálními subtypy značně lišit. 
Nejmenší perikaryon je popsán u granulárních 
buněk mozečku (průměr asi 5 µm), zatímco 
největší těla neuronů patří Betzovým buňkám 
mozku (průměr asi 100 µm). Z pohledu neuronu 
je perikaryon jeho nejmenší částí – většina 
cytoplasmatického obsahu je distribuována do 
axonu a dendritického stromu. Perikaryon je 
bohatý na cisterny endoplasmatického retikula, 
proloženými řetězci volných polyribosomů
označovaných jako Nisslova substance. 
Vzhledem k tomu, že musí vyprodukovat 
veškeré látky potřebné pro fungování a přežití 
neuronu, obsahuje obrovské množství mitochondrií, zdrojů ATP. Dalším typickým znakem 
těla neuronu je přítomnost bohatého cytoskeletu (neurfilament a mikrotubulů), který ve 
vřetenech vybíhá do dendritů a axonů. Podle počtu z těla neuronu odstupujících výběžků lze 
morfologicky dělit neurony na unipolární, bipolární či multipolární:";
            #endregion

            this.SelfTestWord();
            this.SelfTestText(sample);
            this.SelfTestVeta();
        }

        private void SelfTestWord()
        {
            WordDataTable wordTable = new WordDataTable(this.Creator);

            string[] items = new string[] { "první", "pokusný", "text", ",", "pro", "uložení", "do", "databáze", ".", 
                "Druhý", "zkušební", "text", ",", "který", "není", "určen", "do", "databáze", ".",
                "Pokusný", "TEXT", "uloženi", "doufáme", "nebudeme", "." };
            List<WordCls> words = wordTable.Obtain(items);
        }
        private void SelfTestText(string sample)
        {
            decimal time = 0m;
            using (var watch = this.Creator.GetTimer())
            {
                this.AddSpeech(sample, true);
                time = watch.ElapsedMiliSeconds;
            }
        }

        private void SelfTestVeta()
        {
            Veta comparer = new Veta();
            Dictionary<Veta, Veta> vety = new Dictionary<Veta, Veta>(comparer);
            Veta v1a = new Veta() { Slovo1 = "První", Slovo2 = "Pokus", Slovo3 = "Vyhrává" };
            Veta v2a = new Veta() { Slovo1 = "Druhý", Slovo2 = "Pokus", Slovo3 = "Zkouší" };
            Veta v3a = new Veta() { Slovo1 = "Třetí", Slovo2 = "Pokus", Slovo3 = "Prohrává" };
            Veta v4a = new Veta() { Slovo1 = "Čtvrtý", Slovo2 = "Pokus", Slovo3 = "Neexistuje" };
            Veta v1b = new Veta() { Slovo1 = "První", Slovo2 = "Pokus", Slovo3 = "Vyhrává" };
            Veta v2b = new Veta() { Slovo1 = "Druhý", Slovo2 = "Pokus", Slovo3 = "Zkouší" };
            Veta v3b = new Veta() { Slovo1 = "Třetí", Slovo2 = "Pokus", Slovo3 = "Prohrává" };
            Veta v4b = new Veta() { Slovo1 = "Čtvrtý", Slovo2 = "Pokus", Slovo3 = "Neexistuje" };
            Veta v1x = v1a;
            Veta v2x = v2a;
            Veta v3x = v3a;
            Veta v4x = v4a;

            vety.Add(v1a, v1a);
            vety.Add(v2a, v2a);
            vety.Add(v3a, v3a);

            bool c2a = vety.ContainsKey(v2a);
            bool c2b = vety.ContainsKey(v2b);
            bool c2x = vety.ContainsKey(v2x);
            bool c4x = vety.ContainsKey(v4a);

        }

        public class Veta : IEqualityComparer<Veta>
        {
            public string Slovo1;
            public string Slovo2;
            public string Slovo3;
            public override string ToString()
            {
                return Slovo1 + " " + Slovo2 + " " + Slovo3;
            }
            #region IEqualityComparer
            bool IEqualityComparer<Veta>.Equals(Veta x, Veta y)
            {
                bool xn = (x == null);
                bool yn = (y == null);
                if (xn && yn) return true;
                if (xn || yn) return false;
                return (x.Slovo1 == y.Slovo1 &&
                        x.Slovo2 == y.Slovo2 &&
                        x.Slovo3 == y.Slovo3);
            }

            int IEqualityComparer<Veta>.GetHashCode(Veta obj)
            {
                return (obj == null ? 0 :
                        (obj.Slovo1 == null ? 0 : obj.Slovo1.GetHashCode()) ^
                        (obj.Slovo2 == null ? 0 : obj.Slovo2.GetHashCode()) ^
                        (obj.Slovo3 == null ? 0 : obj.Slovo3.GetHashCode()));
            }
            #endregion
        }
        #endregion
        #endregion



        #region Vstup textu
        /// <summary>
        /// Zpracuje vstupující text.
        /// </summary>
        /// <param name="speech"></param>
        public void AddSpeech(string speech, bool isWhole)
        {
            if (speech == null || speech.Length == 0) return;

            SpeechTextProcessor processor = GetSpeechProcessor();
            List<SpeechSentenceCls> sentenceList = processor.ProcessText(speech, isWhole);

            this.AddSentences(sentenceList);
        }
        private void AddSentences(List<SpeechSentenceCls> sentenceList)
        {
            foreach (SpeechSentenceCls sSentence in sentenceList)
            {
                List<WordCls> words = this.Words.Obtain(sSentence.Select<SpeechWordCls, string>(w => w.Text));

                //foreach (SpeechWordCls sWord in sSentence)
                //{
                //    List<WordCls> words = this.MainLanguage.Words.Obtain(sWord.);
                //}
            }
        }
        #endregion
        #region Textové procesory
        /// <summary>
        /// Vrátí procesor zpracování textu.
        /// Procesorů může být více, jeden procesor pro každý thread ("Může na mě mluvit více lidí najednou, každý mluvčí má jeden thread").
        /// </summary>
        /// <returns></returns>
        private SpeechTextProcessor GetSpeechProcessor()
        {
            if (this.SpeechTextProcessors == null)
            {
                lock (this.SpeechTextProcessorLock)
                {
                    if (this.SpeechTextProcessors == null)
                        this.SpeechTextProcessors = new Dictionary<int, SpeechTextProcessor>();
                }
            }

            int threadId = System.Threading.Thread.CurrentThread.ManagedThreadId;
            SpeechTextProcessor processor;
            if (!this.SpeechTextProcessors.TryGetValue(threadId, out processor))
            {
                lock (this.SpeechTextProcessorLock)
                {
                    if (!this.SpeechTextProcessors.TryGetValue(threadId, out processor))
                    {
                        processor = new SpeechTextProcessor(threadId);
                        this.SpeechTextProcessors.Add(threadId, processor);
                    }
                }
            }

            // Čištění:
            if (this.SpeechTextProcessors.Count > 35)
            {
                lock (this.SpeechTextProcessorLock)
                {
                    List<int> removeKeys = this.SpeechTextProcessors
                        .Where(ip => ip.Key != threadId && ip.Value.IsEmpty)
                        .Select(ip => ip.Key)
                        .ToList();
                    foreach (int removeKey in removeKeys)
                        this.SpeechTextProcessors.Remove(removeKey);
                }
            }

            return processor;
        }
        private Dictionary<int, SpeechTextProcessor> SpeechTextProcessors;
        private object SpeechTextProcessorLock = new object();
        #endregion
    }
    #region LanguageCls : jeden konkrétní jazyk (slovník, fráze, věty, celé texty)
    /// <summary>
    /// LanguageCls : jeden konkrétní jazyk (slovník, fráze, věty, celé texty)
    /// </summary>
    public partial class LanguageCls
    {
        #region Konstrukce
        public LanguageCls(SpeechCls speech, string name)
        {
            this.Sentences = new DataTableCls<PhraseCls>(speech.Creator);
        }
        public DataTableCls<PhraseCls> Sentences { get; private set; }
        #endregion
    }
    #endregion
    #region WordCls : jedno slovo, ukládané do databáze
    /// <summary>
    /// WordCls : jedno slovo, ukládané do databáze
    /// </summary>
    [RecordTable("bio.words")]
    [RecordComment("")]
    public class WordCls : RecordCls
    {
        public WordCls()
        {
            this._Name = "";
        }
        public WordCls(string name)
        {
            this._Name = name;
        }
        public string Name { get { return _Name; } set { _Name = value; SetDirty(); } }
        private string _Name;
        public override string ToString()
        {
            return base.ToString() + " " + this.Name;
        }
        protected override void Fill(RecordFillInfo data)
        {
            this.Name = (string)data.Get("name");
        }
        protected override void Save(RecordSaveInfo data)
        {
            data.Add("name", this.Name);
        }
    }
    #endregion
    #region WordDataTable : DataTable pro třídu Word, vyhledává podle ID i podle textu
    /// <summary>
    /// WordDataTable : DataTable pro třídu Word, vyhledává podle ID i podle textu
    /// </summary>
    public class WordDataTable : DataTableCls<WordCls>
    {
        public WordDataTable(CreatorCls creator)
            : base(creator)
        {
            this.Comparer = new StringComparer(StringComparison.CurrentCultureIgnoreCase);
            this.TableName = new Dictionary<string, WordCls>(this.Comparer);
        }
        protected StringComparer Comparer;
        protected Dictionary<string, WordCls> TableName;
        /// <summary>
        /// Metoda zajistí, že pro zadané prvky bude vrácen odpovídající seznam datových objektů.
        /// Konkrétně pro každý vstupní text se vrátí odpovídající objekt WordCls.
        /// Metoda nejprve hledá WordCls v paměti.
        /// Položky, které v paměti nenajde, se pokusí najít v databázi a do paměti je uloží.
        /// Pro zbývající položky, které nenajde v databázi, vytvoří nové objekty, uloží je do databáze a do paměti.
        /// Výsledkem je tedy soupis datových objektů v pořadí odpovídajícím vstupu, přičemž všechny objekty mají platné ID,
        /// odkazující na existující záznam v databázi.
        /// </summary>
        /// <param name="items"></param>
        /// <returns></returns>
        public List<WordCls> Obtain(IEnumerable<string> items)
        {
            List<ObtainItem<string, WordCls>> obtainItems = new List<ObtainItem<string, WordCls>>();
            foreach (string item in items)
                obtainItems.Add(new ObtainItem<string, WordCls>(item, this.TryFindInMemory(item)));

            if (obtainItems.Any(i => !i.Found))
                this.ObtainUsingDb(obtainItems);

            return obtainItems.Select<ObtainItem<string, WordCls>, WordCls>(i => i.Data).ToList();
        }
        
        /// <summary>
        /// Zajistí načtení / vytvoření dat, která dosdu nejsou nalezena.
        /// Tato metoda se volá výhradně, pokud nějaká data dosud nejsou v paměti.
        /// </summary>
        /// <param name="obtainItems"></param>
        protected void ObtainUsingDb(List<ObtainItem<string, WordCls>> obtainItems)
        {
            using (Lock())
            {
                this.ObtainRefresh(obtainItems, false);                                                // V době, kdy jsme čekali na zámek, mohl některý jiný thread načíst data. Tak je zkusíme nyní dohledat...
                if (this.LoadFromDatabase(obtainItems.Where(i => !i.Found).Select(i => i.Key)))        // Prvky, které nejsou v paměti, načteme z databáze (to, co půjde)
                    this.ObtainRefresh(obtainItems, false);                                            // Do seznamu doplníme data pro ty položky, které dosud nebyly nalezeny (=donačteme to, co přišlo z databáze).
                if (this.CreateNewWithDatabase(obtainItems.Where(i => !i.Found).Select(i => i.Key)))   // Prvky, které stále nejsou uloženy v paměti neexistovaly v databázi. Zavedeme je do databáze a do paměti jako nové.
                    this.ObtainRefresh(obtainItems, false);                                            // Do seznamu doplníme položky nově vytvořené.
            }
        }
        /// <summary>
        /// Do soupisu (obtainItems) načte data z paměti, buď všechna (pokud refreshAll == true), nebo pouze chybějící (pokud refreshAll == false: pak je pro prvky kde item.Found == false).
        /// </summary>
        /// <param name="obtainItems"></param>
        /// <param name="refreshAll"></param>
        protected void ObtainRefresh(IEnumerable<ObtainItem<string, WordCls>> obtainItems, bool refreshAll)
        {
            foreach (ObtainItem<string, WordCls> item in obtainItems)
            {
                if (refreshAll || !item.Found)
                    item.Data = this.TryFindInMemory(item.Key);
            }
        }
        /// <summary>
        /// Načte z databáze záznamy pro dané texty. Načtená data uloží standardně do paměti.
        /// </summary>
        /// <param name="items"></param>
        protected bool LoadFromDatabase(IEnumerable<string> items)
        {
            List<string> names = items.Distinct(this.Comparer).ToList();
            if (names.Count == 0) return false;
            int tableCount = this.TableCount;
            List<string> commandTexts = DatabaseCls.CreateSqlBatches(this.ClassInfo.SqlSelect + " WHERE name in {{SqlToken}}", "{{SqlToken}}", names);
            this.LoadDbRecordsWithCommandsNoLock(commandTexts);
            return (this.TableCount > tableCount);
        }
        /// <summary>
        /// Pro dodané texty vytvoří nové objekty a vepíše je do databáze
        /// </summary>
        /// <param name="iEnumerable"></param>
        protected bool CreateNewWithDatabase(IEnumerable<string> items)
        {
            List<string> names = items.Distinct(this.Comparer).ToList();
            if (names.Count == 0) return false;
            int tableCount = this.TableCount;
            foreach (string name in names)
                this.AddNoLock(new WordCls(name));
            return (this.TableCount > tableCount);
        }
        /// <summary>
        /// Pokusí se najít a vrátit záznam pro dané slovo v paměti (v this.TableName), nechodí do databáze.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected WordCls TryFindInMemory(string name)
        {
            WordCls word;
            if (this.TryFindInMemory(name, out word))
                return word;
            return null;
        }
        /// <summary>
        /// Pokusí se najít a vrátit záznam pro dané slovo v paměti (v this.TableName), nechodí do databáze.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        protected bool TryFindInMemory(string name, out WordCls word)
        {
            return this.TableName.TryGetValue(name, out word);
        }
        /// <summary>
        /// Háček pro přidání záznamu do paměti v potomkovi (do jeho indexu)
        /// </summary>
        /// <param name="id"></param>
        /// <param name="record"></param>
        protected override void RecordAddAfter(int id, WordCls record)
        {
            this.TableName.Add(record.Name, record);
        }
        /// <summary>
        /// Komparátor textů použitý pro zdejší hashtable + distincty
        /// </summary>
        protected class StringComparer : IEqualityComparer<string>
        {
            public StringComparer(StringComparison comparison)
            {
                this.Comparison = comparison;
                this.IsCaseSensitive = (comparison == StringComparison.CurrentCultureIgnoreCase || comparison == StringComparison.InvariantCultureIgnoreCase || comparison == StringComparison.OrdinalIgnoreCase);
            }
            protected StringComparison Comparison;
            protected bool IsCaseSensitive;
            #region IEqualityComparer<string> Members
            bool IEqualityComparer<string>.Equals(string x, string y)
            {
                return String.Equals(x, y, this.Comparison);
            }
            int IEqualityComparer<string>.GetHashCode(string obj)
            {
                if (obj == null) return 0;
                return (this.IsCaseSensitive ? obj.GetHashCode() : obj.ToLower().GetHashCode());
            }
            #endregion
        }
    }
    #endregion
    #region PhraseCls : jedna věta / fráze
    [RecordTable("bio.phrases")]
    public class PhraseCls : RecordCls
    {
        public int HashWordsId { get { return _HashWordsId; } set { _HashWordsId = value; SetDirty(); } }
        public int HashWordsName { get { return _HashWordsName; } set { _HashWordsName = value; SetDirty(); } }
        private int _HashWordsId;
        private int _HashWordsName;
        protected override void Fill(RecordFillInfo data)
        {
        }
        protected override void Save(RecordSaveInfo data)
        {
        }
        [EntriesBehavior(EntriesBehaviorMode.Automatic)]
        public EntriesDataList<PhraseItemCls> Items { get; protected set; }
    }
    #endregion
    #region PhraseItemCls : jedna položka věty / fráze
    [RecordTable("bio.phrase_items")]
    public class PhraseItemCls : RecordCls
    {

        protected override void Fill(RecordFillInfo data)
        {
        }
        protected override void Save(RecordSaveInfo data)
        {
        }
    }
    #endregion
}
